﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using MiniJson;
using UnityEngine;

namespace SoarD.Pandora.Console
{
    public partial class PdrConsoleWindow
    {
        [Serializable]
        private sealed class LogWindow : IConsoleWindow
        {
            private LinkedList<LogNode> m_Logs = new LinkedList<LogNode>();
            private LinkedList<LogNode> m_Logs_Search = new LinkedList<LogNode>();
            private Vector2 m_LogScrollPosition = Vector2.zero;
            private Vector2 oldInfoDrag;
            private int m_InfoCount;
            private int m_WarningCount;
            private int m_ErrorCount;
            private int m_FatalCount;
            private int m_xzhCount;
            private int m_wffCount;
            private int m_rgyCount;

            private int m_MaxLine = 10000;
            private bool m_InfoFilter = true;
            private bool m_WarningFilter = true;
            private bool m_ErrorFilter = true;
            private bool m_FatalFilter = true;

            private bool m_xzhFilter = true;
            private bool m_wffFilter = true;
            private bool m_rgyFilter = true;

            private Color32 m_InfoColor = Color.white;
            private Color32 m_WarningColor = Color.yellow;
            private Color32 m_ErrorColor = Color.red;

            private Color32 m_FatalColor = new Color(0.7f, 0.2f, 0.2f);

            private string m_searchKey = string.Empty;

            public int WarningCount
            {
                get
                {
                    return m_WarningCount;
                }
            }

            public int ErrorCount
            {
                get
                {
                    return m_ErrorCount;
                }
            }

            public int FatalCount
            {
                get
                {
                    return m_FatalCount;
                }
            }

            public void Initialize(params object[] args)
            {
                Application.logMessageReceived += OnLogMessageReceived;
            }

            public void Shutdown()
            {
                Application.logMessageReceived -= OnLogMessageReceived;
                Clear();
            }

            public void OnEnter()
            {

            }

            public void OnLeave()
            {

            }

            public void OnUpdate(float elapseSeconds, float realElapseSeconds)
            {
            }

            public void OnDraw()
            {
                getDownPos();
                RefreshCount();

                GUILayout.Space(spaceHeight);
                GUILayout.BeginHorizontal();
                {
                    if (GUILayout.Button("清空", buttonStyle, GUILayout.Width(ButtonSize.x), GUILayout.Height(ButtonSize.y)))
                        Clear();
                    if (GUILayout.Button("存储", buttonStyle, GUILayout.Width(ButtonSize.x), GUILayout.Height(ButtonSize.y)))
                        Save();

                    GUILayout.FlexibleSpace();
                    m_InfoFilter = GUILayout.Toggle(m_InfoFilter, string.Format("Log ({0})", m_InfoCount), buttonStyle, GUILayout.Width(ToggleSize.x), GUILayout.Height(ToggleSize.y));
                    m_WarningFilter = GUILayout.Toggle(m_WarningFilter, string.Format("警告 ({0})", m_WarningCount), buttonStyle, GUILayout.Width(ToggleSize.x), GUILayout.Height(ToggleSize.y));
                    m_ErrorFilter = GUILayout.Toggle(m_ErrorFilter, string.Format("错误 ({0})", m_ErrorCount), buttonStyle, GUILayout.Width(ToggleSize.x), GUILayout.Height(ToggleSize.y));
                    m_FatalFilter = GUILayout.Toggle(m_FatalFilter, string.Format("异常 ({0})", m_FatalCount), buttonStyle, GUILayout.Width(ToggleSize.x), GUILayout.Height(ToggleSize.y));
                    if (m_WarningFilter || m_ErrorFilter || m_FatalFilter)
                    {
                        m_xzhFilter = false;
                        m_wffFilter = false;
                        m_rgyFilter = false;
                    }
                }
                GUILayout.EndHorizontal();

                GUILayout.Space(spaceHeight * 2);
                GUILayout.BeginHorizontal("box");
                GUILayout.Label(string.Format("搜索:{0}", (string.IsNullOrEmpty(m_searchKey) ? string.Empty : m_Logs_Search.Count.ToString())), GUILayout.MaxWidth(100));
                GUI.skin.textField.fontSize = 30;
                string key = GUILayout.TextField(m_searchKey, GUILayout.ExpandWidth(true), GUILayout.Height(40));
                if (key != m_searchKey)
                {
                    m_searchKey = key;
                    m_Logs_Search.Clear();
                    foreach (LogNode log in m_Logs)
                    {
                        if (log.Contains(m_searchKey))
                            m_Logs_Search.AddFirst(log);
                    }
                }
                GUILayout.EndHorizontal();

                buttonStyle.alignment = TextAnchor.UpperLeft;
                GUI.skin.verticalScrollbarThumb.fixedWidth = 50;
                GUI.skin.verticalScrollbar.fixedWidth = 50;
                GUILayout.Space(spaceHeight * 2);
                GUILayout.BeginVertical("box");
                {
                    Vector2 drag = getDrag();
                    if ((drag.x != 0) && (downPos != Vector2.zero))
                    {
                        m_LogScrollPosition.x -= (drag.x - oldInfoDrag.x);
                    }
                    if ((drag.y != 0) && (downPos != Vector2.zero))
                    {
                        m_LogScrollPosition.y += (drag.y - oldInfoDrag.y);
                    }
                    oldInfoDrag = drag;
                    m_LogScrollPosition = GUILayout.BeginScrollView(m_LogScrollPosition);
                    {
                        LinkedList<LogNode> logs = string.IsNullOrEmpty(m_searchKey) ? m_Logs : m_Logs_Search;
                        for (LinkedListNode<LogNode> i = logs.First; i != null; i = i.Next)
                        {
                            LogType logType = i.Value.LogType;

                            if ((logType == LogType.Warning && !m_WarningFilter) ||
                                (logType == LogType.Error && !m_ErrorFilter) ||
                                (logType == LogType.Exception && !m_FatalFilter))
                            {
                                continue;
                            }
                            if (logType == LogType.Log)
                            {
                                LogBelong logBelong = i.Value.LogBelong;

                                if (m_xzhFilter || m_wffFilter || m_rgyFilter)
                                {
                                    if (logBelong == LogBelong.None)
                                    {
                                        continue;
                                    }

                                    if ((logBelong == LogBelong.XZH && !m_xzhFilter) ||
                                        (logBelong == LogBelong.WFF && !m_wffFilter) ||
                                        (logBelong == LogBelong.RGY && !m_rgyFilter))
                                    {
                                        continue;
                                    }
                                }
                                else if (!m_InfoFilter)
                                {
                                    continue;
                                }
                            }

                            GUILayout.BeginVertical("box");
                            {
                                if (GUILayout.Button(GetLogString(i.Value), "Label"))
                                    i.Value.Fold = false;
                                if (!i.Value.Fold)
                                {
                                    GUILayout.BeginHorizontal("box");
                                    if (GUILayout.Button("<复制到剪切板>", "box"))
                                        UniPasteBoard.SetClipBoardString(i.Value.LogMessage);
                                    if (GUILayout.Button("<收起该项>", "box"))
                                        i.Value.Fold = true;
                                    GUILayout.EndHorizontal();
                                }
                            }
                            GUILayout.EndVertical();
                            GUILayout.Space(spaceHeight);
                        }
                    }
                    GUILayout.EndScrollView();
                }
                GUILayout.EndVertical();
                buttonStyle.alignment = TextAnchor.MiddleCenter;
            }

            private void Clear()
            {
                m_Logs.Clear();
                m_Logs_Search.Clear();
            }

            private void Save()
            {
                StringBuilder sb = new StringBuilder();
                foreach (var log in m_Logs)
                {
                    sb.AppendLine(log.LogMessage);
                    sb.AppendLine(log.StackTrack);
                }
                TaskManager.CreateTask(test("192.168.10.116:8888", sb.ToString())).Start();
            }
            IEnumerator test(string url, string form)
            {
                WWW www = new WWW(url, Encoding.UTF8.GetBytes(form));
                yield return www;
            }
            public void RefreshCount()
            {
                m_InfoCount = 0;
                m_WarningCount = 0;
                m_ErrorCount = 0;
                m_FatalCount = 0;
                m_xzhCount = 0;
                m_wffCount = 0;
                m_rgyCount = 0;
                for (LinkedListNode<LogNode> i = m_Logs.First; i != null; i = i.Next)
                {
                    switch (i.Value.LogType)
                    {
                        case LogType.Log:
                            m_InfoCount++;
                            break;
                        case LogType.Warning:
                            m_WarningCount++;
                            break;
                        case LogType.Error:
                            m_ErrorCount++;
                            break;
                        case LogType.Exception:
                            m_FatalCount++;
                            break;
                    }
                    switch (i.Value.LogBelong)
                    {
                        case LogBelong.XZH:
                            m_xzhCount++;
                            break;
                        case LogBelong.WFF:
                            m_wffCount++;
                            break;
                        case LogBelong.RGY:
                            m_rgyCount++;
                            break;
                    }
                }
            }

            private void OnLogMessageReceived(string logMessage, string stackTrace, LogType logType)
            {
                if (logType == LogType.Assert)
                {
                    logType = LogType.Error;
                }

                m_Logs.AddLast(new LogNode(logType, logMessage, stackTrace));
                while (m_Logs.Count > m_MaxLine)
                {
                    m_Logs.RemoveFirst();
                }
            }

            private string GetLogString(LogNode logNode)
            {
                Color32 color = GetLogStringColor(logNode.LogType);
                return string.Format("<color=#{0}{1}{2}{3}>{4}</color>",
                    color.r.ToString("x2"), color.g.ToString("x2"), color.b.ToString("x2"), color.a.ToString("x2"),
                     logNode.LogMessage);
            }

            internal Color32 GetLogStringColor(LogType logType)
            {
                Color32 color = Color.white;
                switch (logType)
                {
                    case LogType.Log:
                        color = m_InfoColor;
                        break;
                    case LogType.Warning:
                        color = m_WarningColor;
                        break;
                    case LogType.Error:
                        color = m_ErrorColor;
                        break;
                    case LogType.Exception:
                        color = m_FatalColor;
                        break;
                }

                return color;
            }

            Vector2 downPos;
            void getDownPos()
            {
                if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer)
                {
                    if (Input.touches.Length == 1 && Input.touches[0].phase == TouchPhase.Began)
                    {
                        downPos = Input.touches[0].position;
                    }
                }
                else
                {
                    if (Input.GetMouseButtonDown(0))
                    {
                        downPos.x = Input.mousePosition.x;
                        downPos.y = Input.mousePosition.y;
                    }
                }
            }

            Vector2 mousePosition;
            Vector2 getDrag()
            {
                if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer)
                {
                    if (Input.touches.Length != 1)
                    {
                        return Vector2.zero;
                    }
                    return Input.touches[0].position - downPos;
                }
                if (Input.GetMouseButton(0))
                {
                    mousePosition = Input.mousePosition;
                    return mousePosition - downPos;
                }
                return Vector2.zero;
            }
        }

        private enum LogBelong
        {
            None,
            XZH,
            WFF,
            RGY
        }

        private sealed class LogNode
        {
            private readonly LogBelong m_LogBelong;
            private readonly LogType m_LogType;
            private readonly string m_LogMessage;
            private readonly string m_StackTrack;
            private readonly string m_LogBrief;
            private bool m_Fold;
            private string m_xzhColor = "yellow";
            private string m_wffColor = "<color=#f";
            private string m_rgyColor = "white";
            private static readonly Regex regex_Brief = new Regex(@"(.+\n){2}");

            public LogNode(LogType logType, string logMessage, string stackTrack)
            {
                m_LogType = logType;
                m_LogMessage = logMessage;
                m_StackTrack = stackTrack;
                m_LogBrief = GetBrief();
                m_LogBelong = LogBelong.None;
                m_Fold = true;
                if (logMessage.ToLower().IndexOf(m_xzhColor, StringComparison.Ordinal) != -1)
                {
                    m_LogBelong = LogBelong.XZH;
                }
                if (logMessage.ToLower().IndexOf(m_wffColor, StringComparison.Ordinal) != -1)
                {
                    m_LogBelong = LogBelong.WFF;
                }
                if (logMessage.ToLower().IndexOf(m_rgyColor, StringComparison.Ordinal) != -1)
                {
                    m_LogBelong = LogBelong.RGY;
                }
            }

            public bool Contains(string key)
            {
                return m_LogMessage.IndexOf(key, StringComparison.CurrentCultureIgnoreCase) != -1;
            }

            public string GetBrief()
            {
                if (regex_Brief.IsMatch(m_LogMessage))
                {
                    string msg = regex_Brief.Match(m_LogMessage).Value;
                    return msg.Substring(0, msg.Length - 1);
                }
                else
                    return m_LogMessage;
            }

            public LogType LogType
            {
                get
                {
                    return m_LogType;
                }
            }

            public LogBelong LogBelong
            {
                get
                {
                    return m_LogBelong;
                }
            }

            public string LogMessage
            {
                get
                {
                    if (Fold)
                        return m_LogBrief;
                    else
                        return m_LogMessage;
                }
            }

            public string StackTrack
            {
                get
                {
                    return m_StackTrack;
                }
            }

            public bool Fold
            {
                get
                {
                    return m_Fold;
                }

                set
                {
                    m_Fold = value;
                }
            }
        }
    }
}